#region Description
/*  File Name:      MEBaseScreen.cs
 *  Author:         Adam Goodchild
 *  
 *  Description:    This class is a single "Screen" to be added to the ScreenManager.
 *                  The BaseScreen can handle Updating and Drawing logic, and can be used
 *                  to draw Menu Systems, including PopUp's, as well as an actual Game Play
 *                  Screen (such as LevelOne, LevelTwo, etc).
 *  
 *  Sources:        The Idea for this class was gained from the following source(s):
 *                  Microsoft XNA Creators Club - Game State Management Sample
*/
#endregion

#region Using Statements
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
using MinimizeEngine.Managers;
#endregion

namespace MinimizeEngine.Screens
{
    #region Enum
    /// <summary>
    /// An Enum describing the Screens Current State
    /// </summary>
    public enum ScreenState
    {
        TransitionOn,
        Active,
        TransitionOff,
        Hidden,
    }
    #endregion

    /// <summary>
    /// See File Description at the top of the File.
    /// </summary>
    public abstract class MEBaseScreen
    {
        #region Fields

        ScreenState mScreenState = ScreenState.TransitionOn;

        bool m_bIsPopup = false;
        bool m_bIsExiting = false;
        bool m_bOtherScreenHasFocus;

        TimeSpan mTransitionOnTime = TimeSpan.Zero;
        TimeSpan mTransitionOffTime = TimeSpan.Zero;

        float m_fTransitionPosition = 1;

        PlayerIndex? mControllingPlayer;

        #endregion

        #region Properties

        /// <summary>
        /// Checks to see whether the Screen is a PopUp or not.
        /// A PopUp defines that the Screen is smaller than the rest, and
        /// it will sit on top of the Screen is was called from. The Screen
        /// it was called from does not have to transition off.
        /// </summary>
        public bool IsPopup
        {
            get { return m_bIsPopup; }
            protected set { m_bIsPopup = value; }
        }

        /// <summary>
        /// Checks to see how long the Screen will take to Transition on
        /// when it has been activated.
        /// </summary>
        public TimeSpan TransitionOnTime
        {
            get { return mTransitionOnTime; }
            protected set { mTransitionOnTime = value; }
        }

        /// <summary>
        /// Checks to see how long the Screen will take to Transition off
        /// when it has been deactivated.
        /// </summary>
        public TimeSpan TransitionOffTime
        {
            get { return mTransitionOffTime; }
            protected set { mTransitionOffTime = value; }
        }

        /// <summary>
        /// Gets the current Position of the Screen Transition.
        /// This can range between the following:
        /// Zero - Fully Active, there is no Transition
        /// One - Fully Off, transitioned to nothing
        /// </summary>
        public float TransitionPosition
        {
            get { return m_fTransitionPosition; }
            protected set { m_fTransitionPosition = value; }
        }

        /// <summary>
        /// Gets the current Alpha of the Screen Transition.
        /// This can range between the following:
        /// 0 - Fully Off, transitioned to nothing
        /// 255 - Fully Active, there is no Transition
        /// </summary>
        public byte TransitionAlpha
        {
            get { return (byte)(255 - TransitionPosition * 255); }
        }

        /// <summary>
        /// Gets the current ScreenState
        /// </summary>
        public ScreenState ScreenState
        {
            get { return mScreenState; }
            protected set{ mScreenState = value; }
        }

        /// <summary>
        /// This indicated that the Screen is Exiting off for good.
        /// The Screen will automatically Remove itself from the ScreenManager
        /// as soon as its done Transitioning.
        /// </summary>
        public bool IsExiting
        {
            get { return m_bIsExiting; }
            protected internal set{ m_bIsExiting = value; }
        }

        /// <summary>
        /// Check to see if the Screen is Active, and if it has Focus
        /// so that it can receive User Input.
        /// </summary>
        public bool IsActive
        {
            get
            {
                return !m_bOtherScreenHasFocus &&
                    (mScreenState == ScreenState.TransitionOn ||
                     mScreenState == ScreenState.Active);
            }
        }

        /// <summary>
        /// Returns the Index of the Player that is controlling the Screen.
        /// Can return Null if the Screen accepts input from any Player.
        /// This is used to ensure that certain Screens only accept Input from 
        /// one Player. But for say the Main Menu, anyone can control the selection.
        /// </summary>
        public PlayerIndex? ControllingPlayer
        {
            get { return mControllingPlayer; }
            internal set{ mControllingPlayer = value; }
        }

        #endregion

        #region Initialization

        /// <summary>
        /// Load the Graphics Content for the Screen
        /// </summary>
        public virtual void LoadContent()
        {

        }

        /// <summary>
        /// UnLoad the Graphics Content for the Screen
        /// </summary>
        public virtual void UnloadContent()
        {

        }

        #endregion

        #region Update

        /// <summary>
        /// Allows the Screen to run its own Logic.
        /// This method is called all of the time, whether it is Active, Hidden
        /// or during a Transition.
        /// </summary>
        /// <param name="gameTime"></param>
        /// <param name="otherScreenHasFocus"></param>
        /// <param name="coveredByOtherScreen"></param>
        public virtual void Update(GameTime gameTime, bool otherScreenHasFocus, bool coveredByOtherScreen)
        {
            m_bOtherScreenHasFocus = otherScreenHasFocus;

            //If the Screen is Exiting
            if(m_bIsExiting)
            {
                //Make sure the Screen Transitions Off
                mScreenState = ScreenState.TransitionOff;

                if(!UpdateTransition(gameTime, mTransitionOffTime, 1))
                {
                    //Once the Screen has fully Transitioned Off, 
                    //remove the Screen from the ScreenManager.
                    MEScreenManager.RemoveScreen(this);

                    m_bIsExiting = false;
                }
            }
            else if (coveredByOtherScreen)
            {
                //If the Screen is covered by another Screen, then it should
                //Transition Off
                if(UpdateTransition(gameTime, mTransitionOffTime, 1))
                {
                    //It is Still Transitioning
                    mScreenState = ScreenState.TransitionOff;
                }
                else
                {
                    //The Transition has finished
                    mScreenState = ScreenState.Hidden;
                }
            }
            else
            {
                //If its none of the above, then the Screen should Transition On,
                //and it will become Active
                if(UpdateTransition(gameTime, TransitionOnTime, -1))
                {
                    //We are still Transitioning
                    mScreenState = ScreenState.TransitionOn;
                }
                else
                {
                    //The Transition has finished
                    mScreenState = ScreenState.Active;
                }
            }
        }

        /// <summary>
        /// This is a Helper Function for Updating the Screens Transition Position.
        /// </summary>
        /// <param name="gameTime"></param>
        /// <param name="time"></param>
        /// <param name="direction"></param>
        /// <returns></returns>
        bool UpdateTransition(GameTime gameTime, TimeSpan time, int transitionDirection)
        {
            //By how much should we Transition?
            float transitionDistance;

            if (time == TimeSpan.Zero)
                transitionDistance = 1;
            else
                transitionDistance = (float)(gameTime.ElapsedGameTime.TotalMilliseconds /
                                          time.TotalMilliseconds);

            //Update the Transition Position.
            m_fTransitionPosition += transitionDistance * transitionDirection;

            //Have we reached the end of the Transition?
            if (((transitionDirection < 0) && (m_fTransitionPosition <= 0)) ||
                ((transitionDirection > 0) && (m_fTransitionPosition >= 1)))
            {
                m_fTransitionPosition = MathHelper.Clamp(m_fTransitionPosition, 0, 1);
                return false;
            }

            //Otherwise we are still transitioning.
            return true;
        }

        /// <summary>
        /// Allow the Screen to handle its own Input. This method is only called
        /// once the Screen is Active, and not when another Screen has got focus.
        /// </summary>
        /// <param name="inputState"></param>
        public virtual void HandleInput(MEInputManager inputManager)
        {
            
        }

        #endregion

        #region Draw
        /// <summary>
        /// This allows the Screen to Draw
        /// </summary>
        /// <param name="gameTime"></param>
        public virtual void Draw(GameTime gameTime)
        {

        }
        #endregion

        #region Public Methods
        
        /// <summary>
        /// The Screen needs to be Removed.
        /// This method allows the Screen to Transition Off, and the it will
        /// call the ScreenManager to Remove itself.
        /// </summary>
        public void ExitScreen()
        {
            if(TransitionOffTime == TimeSpan.Zero)
            {
                //If the Screens Transition Off Time is Zero, then it can be
                //removed straight away.
                MEScreenManager.RemoveScreen(this);
            }
            else
            {
                //Otherwise, the Screen needs to Transition itself Off
                m_bIsExiting = true;
            }
        }
        
        #endregion
    }
}